#include "in.h"

struct MemIndex *MDBCreateIndex( struct MemTable *table , char *index_name , unsigned char is_unique , ... )
{
	struct MemIndex		*index = NULL ;
	va_list			valist ;
	
	int			nret = 0 ;
	
	index = (struct MemIndex *)malloc( sizeof(struct MemIndex) ) ;
	if( index == NULL )
	{
		tls_last_errno = MDB_ERROR_ALLOC ;
		return NULL;
	}
	memset( index , 0x00 , sizeof(struct MemIndex) );
	
	index->index_name_len = strlen(index_name) ;
	if( index->index_name_len >= MDB_INDEX_NAME_MAX_LENGTH )
	{
		tls_last_errno = MDB_ERROR_INDEX_NAME_TOO_LONG ;
		return NULL;
	}
	strcpy( index->index_name , index_name );
	index->is_unique = is_unique ;
	
	index->table = table ;
	
	INIT_LIST_HEAD( & (index->index_datapage_list) );
	
	va_start( valist , is_unique );
	
	TRACE_PRINTF( "ConvertArgsToArray ...\n" );
	nret = ConvertArgsToArray( valist , index->index_field_array , sizeof(index->index_field_array)/sizeof(index->index_field_array[0]) , & (index->index_field_array_count) ) ;
	if( nret )
	{
		TRACE_PRINTF( "ConvertArgsToArray failed[%d]\n" , nret );
		return NULL;
	}
	
	va_end( valist );
	
	list_add_tail( & (index->index_list_node) , & (table->index_list) );
	
	return index;
}

int MDBDropIndex( struct MemTable *table , char *index_name )
{
	struct MemIndex		*index = NULL ;
	
	index = MDBQueryIndex( table , index_name ) ;
	if( index == NULL )
		return (tls_last_errno=MDB_ERROR_INDEX_NOT_FOUND);
	
	return MDBDropIndexDirectly( table , index );
}

int MDBDropIndexDirectly( struct MemTable *table , struct MemIndex *index )
{
	FreeDataPageDirectly( & (index->index_datapage_list) );
	
	list_del( & (index->index_list_node) );
	free( index );
	
	return 0;
}

struct MemIndex *MDBQueryIndex( struct MemTable *table , char *index_name )
{
	size_t				index_name_len ;
	register struct MemIndex	*index = NULL ;
	
	index_name_len = strlen(index_name) ;	
	list_for_each_entry( index , & (table->index_list) , struct MemIndex , index_list_node )
	{
		if( index->index_name_len == index_name_len && MEMCMP( index->index_name , == , index_name , index_name_len ) )
			return index;
	}
	
	return NULL;
}

struct MemIndex *QueryExecuteSchedule( struct MemTable *table , struct MemField *where_field_array , size_t where_field_array_count )
{
	struct MemIndex		*index = NULL ;
	size_t			where_field_array_no ;
	struct MemField		*field = NULL ;
	
	list_for_each_entry( index , & (table->index_list) , struct MemIndex , index_list_node )
	{
		TRACE_PRINTF( "match index[%s]\n" , index->index_name );
		
		if( where_field_array_count > index->index_field_array_count )
		{
			TRACE_PRINTF( "where_field_array_count[%zu] > index->index_field_array_count[%zu]\n" , where_field_array_count , index->index_field_array_count );
			continue;
		}
		
		for( where_field_array_no = 0 , field = index->index_field_array ; where_field_array_no < where_field_array_count ; where_field_array_no++ , field++ )
		{
			TRACE_PRINTF( "match field\n" );
			TRACE_PRINTF( "    where .field_name[%s] .field_offset[%d] .field_size[%zu]\n" , where_field_array[where_field_array_no].field_name , where_field_array[where_field_array_no].field_offset , where_field_array[where_field_array_no].field_size );
			TRACE_PRINTF( "    field .field_name[%s] .field_offset[%d] .field_size[%zu]\n" , field->field_name , field->field_offset , field->field_size );
			if( ! ( where_field_array[where_field_array_no].field_offset == field->field_offset && where_field_array[where_field_array_no].field_size == field->field_size ) )
			{
				TRACE_PRINTF( "match field[%s] failed\n" , where_field_array[where_field_array_no].field_name );
				break;
			}
			else
			{
				TRACE_PRINTF( "match field[%s] success\n" , where_field_array[where_field_array_no].field_name );
			}
		}
		if( where_field_array_no >= where_field_array_count )
		{
			TRACE_PRINTF( "match index[%s] all fields success\n" , index->index_name );
			break;
		}
		else
		{
			TRACE_PRINTF( "match index[%s] failed\n" , index->index_name );
		}
	}
	if( & (index->index_list_node) == & (table->index_list) )
	{
		index = NULL ;
		TRACE_PRINTF( "match all indexs failed\n" );
	}
	else
	{
		TRACE_PRINTF( "match one index[%s]\n" , index->index_name );
	}
	
	return index;
}

int AddIndexDataOnRecord( struct MemTable *table , char *record_dataunit , struct MemIndex *index )
{
	struct MemDataPage	*index_datapage = NULL ;
	struct MemIndexDataUnit	*index_dataunit = NULL ;
	
	int			nret = 0 ;
	
	index_datapage = list_last_entry_or_null( & (index->index_datapage_list) , struct MemDataPage , datapage_list_node ) ;
	TRACE_PRINTF( "index_datapage[%p] .dataunit_used_count[%zu] .dataunit_prealloc_count[%zu]\n" , index_datapage , index_datapage?index_datapage->dataunit_used_count:0 , index_datapage?index_datapage->dataunit_prealloc_count:0 );
	if( index_datapage == NULL || IsDataPageFull(index_datapage) )
	{
		index_datapage = CreateDataPage( index , sizeof(struct MemIndexDataUnit) , & (index->index_datapage_list) , & (index->index_datapage_count) ) ;
		if( index_datapage == NULL )
			return (tls_last_errno=MDB_ERROR_ALLOC);
		
		TRACE_PRINTF( "add index_datapage[%p] to list , .dataunit_size[%zu]\n" , index_datapage , index_datapage->dataunit_size );
	}
	
	index_dataunit = (struct MemIndexDataUnit *)GetUnusedDataUnit( index_datapage ) ;
	TRACE_PRINTF( "AddIndexDataUnitTreeNode index_name[%s] index_dataunit[%p] , record_dataunit[%p] record_data[%p]\n" , index->index_name , index_dataunit , record_dataunit , MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) );
	nret = AddIndexDataUnitTreeNode( record_dataunit , index , index_dataunit ) ;
	if( nret )
	{
		SetDataUnitUnused( index_datapage , (char*)index_dataunit );
		TRACE_PRINTF( "AddIndexDataUnitTreeNode index_name[%s] failed[%d]\n" , index->index_name , nret );
		return nret;
	}
	index_dataunit->index_datapage = index_datapage ;
	index_dataunit->record_dataunit = record_dataunit ;
	
	return 0;
}

int DeleteIndexDataOnRecord( struct MemTable *table , char *record_dataunit , struct MemIndex *index )
{
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	char				*record_data = NULL ;
	struct MemIndexDataUnit		*index_dataunit = NULL ;
	
	struct MemIndexDataUnit		*prev_index_dataunit = NULL ;
	struct MemIndexDataUnit		*next_index_dataunit = NULL ;
	struct MemIndexDataUnit		*travel_index_dataunit = NULL ;
	struct MemRecordDataUnitHead	*travel_record_dataunit_head = NULL ;
	char				*travel_record_data = NULL ;
	int				is_matched ;
	
	record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
	record_data = MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) ;
	
	index_dataunit = QueryIndexDataUnitTreeNode( record_data , index , NULL , 0 ) ;
	if( index_dataunit == NULL )
		return (tls_last_errno=MDB_ERROR_RECORD_NO_INDEX);
	if( index->is_unique )
	{
		travel_record_data = MDB_RECORD_DATAUNIT_TO_DATA(index_dataunit->record_dataunit) ;
		travel_record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
		if( travel_record_dataunit_head->record_no == record_dataunit_head->record_no )
		{
			TRACE_PRINTF( "SetDataUnitUnused index_name[%s] index_dataunit[%p] index->record_dataunit[%p] index->record_data[%p]\n" , index->index_name , index_dataunit , index_dataunit->record_dataunit , MDB_RECORD_DATAUNIT_TO_DATA(index_dataunit->record_dataunit) );
			rb_erase( & (index_dataunit->index_dataunit_tree_node) , & (index->index_dataunit_tree) );
			SetDataUnitUnused( index_dataunit->index_datapage , (char*)index_dataunit );
			return 0;
		}
		else
		{
			return (tls_last_errno=MDB_ERROR_RECORD_NO_INDEX);
		}
	}
	else
	{
		travel_index_dataunit = index_dataunit ;
		for( ; ; )
		{
			prev_index_dataunit = GetPrevIndexDataUnit( travel_index_dataunit ) ;
			if( prev_index_dataunit == NULL )
				break;
			travel_record_data = MDB_RECORD_DATAUNIT_TO_DATA(travel_index_dataunit->record_dataunit) ;
			travel_record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
			if( travel_record_dataunit_head->record_no == record_dataunit_head->record_no )
			{
				TRACE_PRINTF( "SetDataUnitUnused index_name[%s] index_dataunit[%p] index->record_dataunit[%p] index->record_data[%p]\n" , index->index_name , index_dataunit , index_dataunit->record_dataunit , MDB_RECORD_DATAUNIT_TO_DATA(index_dataunit->record_dataunit) );
				rb_erase( & (index_dataunit->index_dataunit_tree_node) , & (index->index_dataunit_tree) );
				SetDataUnitUnused( index_dataunit->index_datapage , (char*)index_dataunit );
				return 0;
			}
			is_matched = TestRecordMatched( travel_record_data , index->index_field_array , index->index_field_array_count , record_data ) ;
			if( is_matched == 0 )
				break;
			
			travel_index_dataunit = prev_index_dataunit ;
		}
		
		travel_index_dataunit = index_dataunit ;
		while( travel_index_dataunit )
		{
			next_index_dataunit = GetNextIndexDataUnit( travel_index_dataunit ) ;
			
			if( travel_index_dataunit > index_dataunit )
			{
				travel_record_data = MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) ;
				is_matched = TestRecordMatched( travel_record_data , index->index_field_array , index->index_field_array_count , record_data ) ;
				if( is_matched == 0 )
					break;
			}
			
			travel_record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
			if( travel_record_dataunit_head->record_no == record_dataunit_head->record_no )
			{
				TRACE_PRINTF( "SetDataUnitUnused index_name[%s] index_dataunit[%p] index->record_dataunit[%p] index->record_data[%p]\n" , index->index_name , index_dataunit , index_dataunit->record_dataunit , MDB_RECORD_DATAUNIT_TO_DATA(index_dataunit->record_dataunit) );
				rb_erase( & (index_dataunit->index_dataunit_tree_node) , & (index->index_dataunit_tree) );
				SetDataUnitUnused( index_dataunit->index_datapage , (char*)index_dataunit );
				
				return 0;
			}
			
			travel_index_dataunit = next_index_dataunit ;
		}
		
		return (tls_last_errno=MDB_ERROR_RECORD_NO_INDEX);
	}
}

int AddIndexDataUnitTreeNode( char *add_record_dataunit , struct MemIndex *index , struct MemIndexDataUnit *dataunit )
{
	char			*add_record_data = NULL ;
	struct rb_node		**pp_add_node = NULL ;
	struct rb_node		*p_parent = NULL ;
	struct MemIndexDataUnit	*travel_index_dataunit = NULL ;
	char			*travel_record_dataunit = NULL ;
	char			*travel_record_data = NULL ;
	int			is_matched ;
	
	add_record_data = MDB_RECORD_DATAUNIT_TO_DATA(add_record_dataunit) ;
	
	pp_add_node = & (index->index_dataunit_tree.rb_node) ;
	while( *pp_add_node )
	{
		p_parent = (*pp_add_node) ;
		
		travel_index_dataunit = container_of( *pp_add_node , struct MemIndexDataUnit , index_dataunit_tree_node ) ;
		travel_record_dataunit = travel_index_dataunit->record_dataunit ;
		travel_record_data = MDB_RECORD_DATAUNIT_TO_DATA(travel_record_dataunit) ;
		TRACE_PRINTF( "travel (*pp_add_node)[%p] index_dataunit[%p] record_dataunit[%p] record_data[%p]\n" , (*pp_add_node) , travel_index_dataunit , travel_record_dataunit , travel_record_data );
		is_matched = TestRecordMatched( travel_record_data , index->index_field_array , index->index_field_array_count , add_record_data ) ;
		TRACE_PRINTF( "TestRecordMatched return is_matched[%d]\n" , is_matched );
		if( is_matched < 0 )
		{
			pp_add_node = & ((*pp_add_node)->rb_left) ;
		}
		else if( is_matched > 0 )
		{
			pp_add_node = & ((*pp_add_node)->rb_right) ;
		}
		else
		{
			if( index->is_unique )
				return (tls_last_errno=MDB_ERROR_INDEX_DUPLICATED);
			pp_add_node = & ((*pp_add_node)->rb_right) ;
		}
	}
	
	rb_link_node( & (dataunit->index_dataunit_tree_node) , p_parent , pp_add_node );
	rb_insert_color( & (dataunit->index_dataunit_tree_node) , & (index->index_dataunit_tree) );
	
	return 0;
}

struct MemIndexDataUnit *QueryIndexDataUnitTreeNode( char *where_record , struct MemIndex *index , struct MemField *field_array , size_t field_array_count )
{
	struct rb_node		**pp_add_node = NULL ;
	struct MemIndexDataUnit	*travel_index_dataunit = NULL ;
	char			*travel_record_dataunit = NULL ;
	char			*travel_record_data = NULL ;
	int			is_matched ;
	
	if( field_array == NULL )
	{
		field_array = index->index_field_array ;
		field_array_count = index->index_field_array_count ;
	}
	
	pp_add_node = & (index->index_dataunit_tree.rb_node) ;
	while( *pp_add_node )
	{
		travel_index_dataunit = container_of( *pp_add_node , struct MemIndexDataUnit , index_dataunit_tree_node ) ;
		travel_record_dataunit = travel_index_dataunit->record_dataunit ;
		travel_record_data = MDB_RECORD_DATAUNIT_TO_DATA(travel_record_dataunit) ;
		
		TRACE_PRINTF( "travel_record_data[%p]\n" , travel_record_data );
		is_matched = TestRecordMatched( travel_record_data , field_array , field_array_count , where_record ) ;
		TRACE_PRINTF( "TestRecordMatched return is_matched[%d]\n" , is_matched );
		if( is_matched < 0 )
		{
			pp_add_node = & ((*pp_add_node)->rb_left) ;
		}
		else if( is_matched > 0 )
		{
			pp_add_node = & ((*pp_add_node)->rb_right) ;
		}
		else
		{
			return travel_index_dataunit;
		}
	}
	
	return NULL;
}

struct MemIndexDataUnit *GetPrevIndexDataUnit( struct MemIndexDataUnit *index_dataunit )
{
	struct rb_node		*prev_rbtree_node = NULL ;
	
	prev_rbtree_node = rb_prev( & (index_dataunit->index_dataunit_tree_node) ) ;
	if( prev_rbtree_node == NULL )
		return NULL;
	return container_of( prev_rbtree_node , struct MemIndexDataUnit , index_dataunit_tree_node ) ;
}

struct MemIndexDataUnit *GetNextIndexDataUnit( struct MemIndexDataUnit *index_dataunit )
{
	struct rb_node		*next_rbtree_node = NULL ;
	
	next_rbtree_node = rb_next( & (index_dataunit->index_dataunit_tree_node) ) ;
	if( next_rbtree_node == NULL )
		return NULL;
	return container_of( next_rbtree_node , struct MemIndexDataUnit , index_dataunit_tree_node ) ;
}

